﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;
using System.Diagnostics;

namespace ViewMaker.Core
{
    /// <summary>
    /// リレーコマンド
    /// </summary>
    public class RelayCommand : ICommand
    {
        private readonly Action<object> _action;
        private readonly Predicate<object> _canExecute;
        private readonly ViewModelBase _vm;

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="vm">オーナーのViewModel</param>
        /// <param name="action">実行する処理</param>
        /// <param name="canExecute">実行可能か判断する処理</param>
        public RelayCommand(ViewModelBase vm, Action<object> action, Predicate<object> canExecute = null)
            : this(action, canExecute)
        {
            this._vm = vm;
        }

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="action">実行する処理</param>
        /// <param name="canExecute">実行可能か判断する処理</param>
        public RelayCommand(Action<object> action, Predicate<object> canExecute = null)
        {
            if (action == null) throw new ArgumentNullException("action");
            _action = action;
            _canExecute = canExecute;
        }

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="vm">オーナーのViewModel</param>
        /// <param name="action">実行する処理</param>
        public RelayCommand(ViewModelBase vm, Action action, Predicate<object> canExecute = null)
            : this(action, canExecute)
        {
            this._vm = vm;
        }

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="action">実行する処理</param>
        public RelayCommand(Action action, Predicate<object> canExecute = null)
        {
            if (action == null) throw new ArgumentNullException("action");
            _action = delegate { action(); };
            _canExecute = canExecute;
        }

        /// <summary>
        /// コマンドが実行可能か判断する。
        /// </summary>
        /// <param name="parameter">パラメータ</param>
        /// <returns></returns>
        public bool CanExecute(object parameter)
        {
            return _canExecute == null ? true : _canExecute(parameter);
        }
#if !SILVERLIGHT
        /// <summary>
        /// CanExecute変化イベント
        /// </summary>
        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }
#else
        /// <summary>
        /// CanExecute変化イベント
        /// </summary>
        public event EventHandler CanExecuteChanged;

        /// <summary>
        /// CanExecute変化イベントを通知する
        /// </summary>
        /// <param name="arg">イベント引数</param>
        public virtual void RaiseCanExecuteChanged(EventArgs arg)
        {
            if (CanExecuteChanged != null) CanExecuteChanged(this, arg);
        }
#endif

        /// <summary>
        /// コマンドを実行する
        /// </summary>
        /// <param name="parameter">パラメータ</param>
        public void Execute(object parameter)
        {
            Exception exc = null;
            try
            {
                if (_vm != null) _vm.OnCommandExecuting(this, parameter);
                _action(parameter);
            }
            catch (Exception ex)
            {
                exc = ex;
            }
            finally
            {
                if (_vm != null) _vm.OnCommandExecuted(this, exc);
            }
        }
    }
}
